package com.redis.interceptor;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.data.redis.core.RedisTemplate;

/**
 * 
 * 使用面向切面的方式配置缓存
 *
 */
public class MethodCacheInterceptor implements MethodInterceptor {

	private RedisTemplate<Serializable, Object> redisTemplate;

	/**
	 * 
	 * 默认缓存过期时间
	 * 
	 */
	private long defaultCacheExpireTime = 300L;

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Object value = null;
		String targetName = invocation.getThis().getClass().getName();
		String methodName = invocation.getMethod().getName();

		Object[] arguments = invocation.getArguments();
		String key = getCacheKey(targetName, methodName, arguments);

		if (exists(key)) {
			return getCache(key);
		} else {
			// 写入缓存
			final Object tValue = invocation.proceed();
			value = tValue;
			if (tValue != null) {
				new Thread(() -> {
					setCache(key, tValue, defaultCacheExpireTime);
				}).start();
			}
		}
		return value;
	}

	/**
	 * 创建缓存key
	 * 
	 * @param targetName
	 * @param methodName
	 * @param arguments
	 * @return
	 */
	private String getCacheKey(final String targetName, final String methodName, final Object[] arguments) {
		StringBuilder sb = new StringBuilder();
		sb.append(targetName).append("_").append(methodName);
		if (arguments != null) {

			for (int i = 0; i < arguments.length; i++) {
				sb.append("_").append(arguments[i]);
			}
		}
		return sb.toString();
	}

	/**
	 * 判断缓存中是否有对应的value
	 * 
	 * @param key
	 * @return
	 */
	public boolean exists(final String key) {
		return redisTemplate.hasKey(key);
	}

	/**
	 * 读取缓存
	 * 
	 * @param key
	 * @return
	 */
	public Object getCache(final String key) {
		return redisTemplate.opsForValue().get(key);
	}

	/**
	 * 写入缓存
	 * 
	 * @param key
	 * @param value
	 * @param timeout
	 * @return
	 */
	public boolean setCache(String key, Object value, long timeout) {
		boolean result = false;

		try {
			redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public void setRedisTemplate(RedisTemplate<Serializable, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
}
